﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Derbster.Domain.Test
{
    [TestClass]
    public class MatchPenaltyUnitTest : ContractUnitTest
    {
        private Match _testMatch;
        private CommandManager _commandManager;
        [TestInitialize]
        public void TestSetup()
        {
            _testMatch = TestHelper.CreateTestMatch(true, true, "TestMatch");
            _commandManager = new CommandManager();
            _commandManager.Clear();
        }
        
        [TestMethod]
        public void TestSkaterPenalty()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            Assert.IsTrue(_testMatch.Penalties.Any(
                p=>p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player
                && p.Code == "B"));
            Assert.IsTrue(player.State == PlayerState.InQueue);
            _commandManager.Undo();
            Assert.IsFalse(_testMatch.Penalties.Any());
            Assert.IsTrue(player.State == PlayerState.Ready);
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            Assert.IsTrue(player.State == PlayerState.InQueue);
        }

        [TestMethod]
        public void TestSkaterPenaltyBadPlayer()
        {
            bool throws = false;

            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            var player2 = _testMatch.FirstTeamRoster.Players.Skip(3).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            try
            {
                _testMatch.SkaterPenalty(player2, "B");
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestCorrectSkaterPenalty()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            _testMatch.CorrectPenalty(player, 1, "F");
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
            _commandManager.Undo();
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
        }

        [TestMethod]
        public void TestCorrectSkaterPenaltyWithOtherSkater()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            var player2 = _testMatch.FirstTeamRoster.Players.Skip(4).First();

            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player2);

            _testMatch.SkaterPenalty(player, "B");
            _testMatch.SkaterPenalty(player2, "M");
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            Assert.IsTrue(_testMatch.Penalties.Any(
              p => p.JamNumber == _testMatch.CurrentJamNumber
              && p.Player == player2
              && p.Code == "M"));
            _testMatch.CorrectPenalty(player, 1, "F");
            Assert.IsTrue(_testMatch.Penalties.Count == 2);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
            Assert.IsTrue(_testMatch.Penalties.Any(
              p => p.JamNumber == _testMatch.CurrentJamNumber
              && p.Player == player2
              && p.Code == "M"));
            _commandManager.Undo();
            Assert.IsTrue(_testMatch.Penalties.Count == 2);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            Assert.IsTrue(_testMatch.Penalties.Any(
              p => p.JamNumber == _testMatch.CurrentJamNumber
              && p.Player == player2
              && p.Code == "M"));
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.Penalties.Count == 2);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
            Assert.IsTrue(_testMatch.Penalties.Any(
              p => p.JamNumber == _testMatch.CurrentJamNumber
              && p.Player == player2
              && p.Code == "M"));
        }

        [TestMethod]
        public void TestCorrectSkaterPenaltyBadPlayer()
        {
            bool throws = false;

            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            try
            {
                var player2 =  _testMatch.FirstTeamRoster.Players.Skip(3).First();
                _testMatch.CorrectPenalty(player2, 1, "F");
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestCorrectSkaterPenaltyBadRank()
        {
            bool throws = false;

            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            try
            {
                var player2 = _testMatch.FirstTeamRoster.Players.Skip(3).First();
                _testMatch.CorrectPenalty(player2, 2, "F");
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }


        [TestMethod]
        public void TestCorrectLastSkaterPenalty()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            _testMatch.CorrectLastPenalty(player, "F");
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
            _commandManager.Undo();
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "F"));
        }


        [TestMethod]
        public void TestRemoveLastPenalty()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            _testMatch.RemoveLastPenalty(player);
            Assert.IsFalse(_testMatch.Penalties.Any(p => p.Player == player));
            Assert.IsTrue(player.State == PlayerState.Ready);
            _commandManager.Undo();
            Assert.IsTrue(_testMatch.Penalties.Count == 1);
            Assert.IsTrue(_testMatch.Penalties.Any(
               p => p.JamNumber == _testMatch.CurrentJamNumber
               && p.Player == player
               && p.Code == "B"));
            Assert.IsTrue(player.State == PlayerState.InQueue);
            _commandManager.Redo();
            Assert.IsFalse(_testMatch.Penalties.Any(p => p.Player == player));
            Assert.IsTrue(player.State == PlayerState.Ready);
        }

        [TestMethod]
        public void TestRemoveLastPenaltyNoPenalty()
        {
            bool throws = false;
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            try
            {
                _testMatch.RemoveLastPenalty(player);
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestSkaterSeated()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            _testMatch.PlayerSeated(player);
            Assert.IsTrue(_testMatch.BoxEvents.Any(
                p => p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player));
            Assert.IsTrue(player.State == PlayerState.InBox);
            _commandManager.Undo();
            Assert.IsFalse(_testMatch.BoxEvents.Any());
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.BoxEvents.Any(
                p => p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player));
        }

        [TestMethod]
        public void TestSkaterReleased()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            _testMatch.PlayerSeated(player);
            _testMatch.PlayerReleased(player);
            Assert.AreEqual(2,_testMatch.BoxEvents.Count(
                p => p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player));
            Assert.IsTrue(player.State == PlayerState.Ready);
            _commandManager.Undo();
            Assert.AreEqual(1,_testMatch.BoxEvents.Count(
                p => p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player));
            Assert.IsTrue(player.State == PlayerState.InBox);
            _commandManager.Redo();
            Assert.AreEqual(2,_testMatch.BoxEvents.Count(
                p => p.JamNumber == _testMatch.CurrentJamNumber
                && p.Player == player));
            Assert.IsTrue(player.State == PlayerState.Ready);
        }
        [TestMethod]
        public void TestSkaterReleasedNotSeated()
        {
            bool thrown = false;
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var player = _testMatch.FirstTeamRoster.Players.Skip(1).First();
            _testMatch.CurrentJam.FirstTeamLineup.AddBlocker(player);
            _testMatch.SkaterPenalty(player, "B");
            try
            {
                _testMatch.PlayerReleased(player);
            }
            catch (Exception )
            {
                thrown = true;
            }
            Assert.IsTrue(thrown);
        }
    }
}
